/*
 * @Description: 引擎-相机功能相关函数
 */

import { isExist, vector3Offset,_offsetToposition } from '../tools/index.js'
import { getEntityById, globeCollection } from './map_collection.js'

/**
 * 设置地下视图启用状态
 *
 * @param {boolean} value - 一个布尔值，表示是否启用地下视图功能。
 * @returns {void} 该函数不返回任何值。
 *
 * @description
 * 此函数用于控制场景中主相机的地下视图功能。如果 `value` 为 `true`，则允许相机查看地表以下的景象；
 * 如果为 `false`，则相机将不能穿透地面看到地下。这对于具有地形遮挡或需要控制视觉层次的3D应用特别有用。
 *
 * @example
 * // 启用地下视图
 * setEnableUnderGround(true);
 *
 * // 禁用地下视图
 * setEnableUnderGround(false);
 */
function setEnableUnderGround(value) {
	GlobalViewer.scene.mainCamera.cameraController().enableUnderGround = value
}

/**
 * 控制相机平滑飞向指定经纬度位置，可选距离偏移、飞行持续时间及视角方向设置。
 *
 * @param {Object} options - 飞行参数对象。
 * @param {number} options.lat - 目标位置的纬度。
 * @param {number} options.lng - 目标位置的经度。
 * @param {number} options.alt - 目标位置的海拔高度。
 * @param {number} [options.heading=25.2] - 相机初始朝向角度，默认为正北方向（0度）。
 * @param {number} [options.pitch=-16.5] - 相机俯仰角度，默认值为-16.5度。
 * @param {number} [options.roll=0] - 相机翻滚角度，默认为0度。
 * @param {number} [options.duration=3] - 飞行持续时间（秒），默认为3秒。
 * @param {number} [options.distance] - 相对于目标点的垂直偏移距离（单位：米）。若提供，将计算偏移后的飞行终点。
 *
 * @throws {Error} 如果未提供纬度、经度或海拔参数，则抛出错误。
 *
 * @description
 * 本函数允许用户指定经纬度和海拔高度来控制相机平滑飞向指定位置。用户还可以自定义飞行的持续时间、视角的初始朝向、俯仰和翻滚角度。
 * 特别地，当提供`distance`参数时，相机将飞向目标点正上方的偏移位置，此时`heading`固定为正北方向且不考虑`roll`参数。
 * 若不提供`distance`，则直接飞向目标点，并可自由设置`heading`、`pitch`和`roll`。
 */
function flyTo({ lat, lng, alt, heading, pitch, roll, duration, distance }) {
	if (!isExist(lat) || !isExist(lng) || !isExist(alt)) {
		// throw new Error('参数错误 经度 纬度 海拔')
		console.error("参数错误 经度 纬度 海拔")
		return
	}

	const point = SSmap.Cartesian3.fromDegrees(lng, lat, alt).toVector3()
	const cameraController = GlobalViewer.scene.mainCamera.cameraController()
	if (isExist(distance)) {
		const Y = Math.sin(((90 + pitch) * Math.PI) / 180) * distance
		const Z = Math.cos(((90 + pitch) * Math.PI) / 180) * distance
		const destination = vector3Offset(point, {
			offsetY: -Y,
			offsetZ: Z,
		})
		cameraController.flyTo(
			destination,
			isExist(duration) ? duration : 3,
			0, // 不能改 0为正北方向
			isExist(pitch) ? pitch : -16.5,
			0 // 不能改
		)
		return
	}
	if (isExist(duration)) {
		cameraController.flyTo(
			point,
			isExist(duration) ? duration : 3,
			isExist(heading) ? heading : 25.2,
			isExist(pitch) ? pitch : -16.5,
			isExist(roll) ? roll : 0
		)
	} else {
		cameraController.setView(
			SSmap.Cartesian3.fromDegrees(lng, lat, alt),
			isExist(heading) ? heading * (Math.PI / 180) : 0,
			isExist(pitch) ? pitch * (Math.PI / 180) : -89,
			isExist(roll) ? roll * (Math.PI / 180) : 0
		)
	}
}

function flyToGeoPointsExtent(geoPoints) {
	const rectangle = SSmap.Rectangle.fromDegrees(0, 0, 0, 0)
	geoPoints.forEach((p) =>
		rectangle.combinePoint(SSmap.Cartographic.fromDegrees(p.lng, p.lat, p.alt))
	)
	// 计算矩形的四个顶点：西南、西北、东北、东南
	const sw = rectangle.southwest().toVector3()
	const nw = rectangle.northwest().toVector3()
	const ne = rectangle.northeast().toVector3()
	const se = rectangle.southeast().toVector3()

	// 再次将顶点坐标合并到矩形中，以确保矩形的准确计算
	rectangle.combinePoint(sw.toCartesian3().toCartographic())
	rectangle.combinePoint(nw.toCartesian3().toCartographic())
	rectangle.combinePoint(ne.toCartesian3().toCartographic())
	rectangle.combinePoint(se.toCartesian3().toCartographic())

	const cameraController = window.viewer.scene.mainCamera.cameraController()
	cameraController.flyToRectangle(rectangle, 2, 0, -90, 0)
}

function flyToEntityById({id,dataType,opt={}}){
	if (!isExist(id)) {
		console.error('id 错误')
		return
	}
	if (!isExist(dataType)) {
		console.error('dataType 错误')
		return
	}
   if (dataType === 'Tileset'){
		let tileset = globeCollection.tileset[id]
		if(tileset){
			  let bounds = tileset.rectangle.bounds();
        let Cartographic = tileset.rectangle.center();
				let height = bounds.radius <= 0 ? 1 : bounds.radius;
				// let position = bounds.center.toCartographic()
				flyToredraw(Cartographic, height, opt);
		}				
	 }else if(dataType === 'Model'){
		let model = globeCollection.models[id]
		let renderer = null
		const stack = []
		stack.push(model.entity)
		while (stack.length) {
			const entity = stack.pop()
			if (entity.renderer) {
				renderer = entity.renderer
				break
			}
			const children = entity.childEntities
			for (let i = 0; i < children.size(); i++) {
				stack.push(children.get(i))
			}
		}
		//定位
		if(renderer){
	        console.log("renderer.boundingVolume",renderer.boundingVolume);
			let Cartographic = renderer.boundingVolume.center.toCartographic();
			let height = renderer.boundingVolume.boundingSphere.radius <= 0 ? 1 : renderer.boundingVolume.boundingSphere.radius;
			// let position = bounds.center.toCartographic()
			flyToredraw(Cartographic, height, opt);
		}
		
	 }
}

function flyToredraw(Cartographic, height, opt){
      let nowposition = _offsetToposition(Cartographic, height, opt);
      let newPosition = nowposition.toCartographic().toDegrees()
			flyTo({
				lat:newPosition.lat, lng:newPosition.lon, alt:newPosition.height,
				duration: Number(opt.duration) || 3,
				heading: Number(opt.heading) || 0,
				pitch: Number(opt.pitch) == 0 ? 0 : Number(opt.pitch) || -45,
				roll: Number(opt.roll) || 0,
			});
}

function flyToByEntity(id, observation = {}) {
	let target = getEntityById(id)
	if (!target) throw new Error('id is not exist')

	if (target.dataType === 'Tileset') {
		const bounds = target.rectangle.bounds()
		const cartographic = target.rectangle.center()
		if (!bounds || !bounds.radius) throw new Error('bounds is not exist')
		if (!cartographic) throw new Error('center is not exist')
		const position = cartographic.toDegrees()
		flyTo({
			lat: position.lat,
			lng: position.lon,
			alt: cartographic.height + (observation.alt || 0),
			pitch: observation.pitch || -15,
			duration: observation.duration || 3,
			distance: (Number(bounds.radius) / 2) * 3,
		})
	}

	if (target.dataType === 'TilesetLayer') {
		const bounds = target.tileset().rectangle.bounds()
		const cartographic = target.tileset().rectangle.center()
		if (!bounds || !bounds.radius) throw new Error('bounds is not exist')
		if (!cartographic) throw new Error('center is not exist')
		const position = cartographic.toDegrees()
		flyTo({
			lat: position.lat,
			lng: position.lng,
			alt: cartographic.height,
			pitch: -45,
			duration: 3,
			distance: (Number(bounds.radius) / 2) * 3,
		})
	}

	if (target.dataType === 'Model') {
		target.readyPromise.then(() => {
			let renderer = null
			const stack = []
			stack.push(target.entity)
			while (stack.length) {
				const entity = stack.pop()
				if (entity.renderer) {
					renderer = entity.renderer
					break
				}
				const children = entity.childEntities
				for (let i = 0; i < children.size(); i++) {
					stack.push(children.get(i))
				}
			}

			if (renderer) {
				const position = renderer.boundingVolume.center.toCartographic().toDegrees()
				const boundingSphere = renderer.boundingVolume.boundingSphere
				const radius = boundingSphere.radius
				flyTo({
					lat: position.lat,
					lng: position.lng,
					alt: position.height,
					pitch: -45,
					duration: 3,
					distance: (Number(radius) / 2) * 3,
				})
			}
		})
	}
}

/**
 * 获取当前相机在三维场景中的地理坐标位置及角度信息。
 *
 * @param {function} [cb] - 可选的回调函数，用于接收获取到的相机位置及角度信息对象。
 *
 * @returns {{lat: number, lng: number, alt: number, heading: number, pitch: number}} 一个对象，包含相机的地理坐标（纬度`lat`、经度`lng`、海拔高度`alt`）以及视角的俯仰角`pitch`和偏航角`heading`，所有角度以度为单位。
 *
 * @description
 * 该函数从全局视图器的主要相机控制器中检索相机的实时位置信息（经纬度、海拔）及视角方向（俯仰角、偏航角）。
 * 角度值由弧度转换为度。结果直接返回，并且如果提供了回调函数`cb`，也会通过该回调传递数据。
 *
 * @example
 * const positionInfo = getCameraGeoPositionAndAngles();
 * console.log(positionInfo);
 *
 * // 使用回调方式
 * getCameraGeoPositionAndAngles(info => {
 *   console.log(`当前位置：经度 ${info.lng}，纬度 ${info.lat}，海拔 ${info.alt}m`);
 *   console.log(`视角方向：俯仰角 ${info.pitch}°，偏航角 ${info.heading}°`);
 * });
 */
function getCameraGeoPositionAndAngles(cb) {
	const cameraController = GlobalViewer.scene.mainCamera.cameraController()
	const position = cameraController.positionCartographic.toDegrees()
	const result = {
		lat: position.latitude,
		lng: position.longitude,
		alt: position.height,
		heading: cameraController.heading * (180 / Math.PI),
		pitch: cameraController.pitch * (180 / Math.PI),
	}

	if (cb && typeof cb === 'function') {
		cb(result)
	}

	return result
}

function fog(value) {
	window.viewer.scene.fog.enabled = value
}

// 二三维场景模式切换
function toggleSceneMode(duration = 1.5) {
	const scene = window.viewer.scene
	const camera = scene.mainCamera
	const cameraController = camera.cameraController()

	if (cameraController.enableTilt) {
		cameraController.flyToCartographic(
			cameraController.positionCartographic,
			duration, //持续时间
			0, //heading
			-90, //pitch
			0 //roll
		)
	} else {
		cameraController.flyToCartographic(
			cameraController.positionCartographic,
			duration, //持续时间
			0, //heading
			-45, //pitch
			0 //roll
		)
	}
	cameraController.enableTilt = !cameraController.enableTilt
}

function cameraMoveForward(value) {
	const cameraController = window.viewer.scene.mainCamera.cameraController()
	cameraController.moveForward(value)
}

function cameraMoveBackward(value) {
	const cameraController = window.viewer.scene.mainCamera.cameraController()
	cameraController.moveBackward(value)
}

function cameraMoveLeft(value) {
	const cameraController = window.viewer.scene.mainCamera.cameraController()
	cameraController.moveLeft(value)
}

function cameraMoveRight(value) {
	const cameraController = window.viewer.scene.mainCamera.cameraController()
	cameraController.moveRight(value)
}

function cameraLookLeft(radian) {
	const cameraController = window.viewer.scene.mainCamera.cameraController()
	cameraController.lookLeft(radian)
}

function cameraLookRight(radian) {
	const cameraController = window.viewer.scene.mainCamera.cameraController()
	cameraController.lookRight(radian)
}

function setCameraController({
	enableInputs,
	enableZoom,
	enableRotate,
	enablePan,
	enableLook,
}) {
	const cameraController = window.viewer.scene.mainCamera.cameraController()
	// 获取或者设置是否允许鼠标输入修改相机位置和方向，默认：true
	if (isExist(enableInputs)) cameraController.enableInputs = enableInputs
	// 获取或者设置是否允许相机缩放，默认：true
	if (isExist(enableZoom)) cameraController.enableZoom = enableZoom
	if (isExist(enableRotate)) cameraController.enableRotate = enableRotate
	if (isExist(enablePan)) cameraController.enablePan = enablePan
	if (isExist(enableLook)) cameraController.enableLook = enableLook
}

function createFrameAction({ id, onTriggered }, cb = null) {
	if (!isExist(id)) throw new Error('id is not exist')

	if (globeCollection['frameAction'][id]) throw new Error('frameAction already exists')
	const frameAction = new SSmap.FrameAction()
	frameAction.onTriggered(onTriggered)
	window.viewer.scene.rootEntity.addComponent(frameAction)
	globeCollection['frameAction'][id] = frameAction
	if (typeof cb === 'function') cb()
}

function removeFrameAction({ id }) {
	if (!isExist(id)) throw new Error('id is not exist')
	if (!globeCollection['frameAction'][id]) throw new Error('frameAction id not exists')
	const frameAction = globeCollection['frameAction'][id]
	frameAction.delete()
	delete globeCollection['frameAction'][id]
}

export {
	setEnableUnderGround,
	flyTo,
	flyToGeoPointsExtent,
	getCameraGeoPositionAndAngles,
	fog,
	flyToByEntity,
	toggleSceneMode,
	setCameraController,
	cameraMoveForward,
	cameraMoveBackward,
	cameraMoveLeft,
	cameraMoveRight,
	cameraLookLeft,
	cameraLookRight,
	createFrameAction,
	removeFrameAction,
	flyToEntityById
}
